1 module kprop.api.digitalocean.digitalocean; 2 import std.stdio; 3 import std.json; 4 import std.net.curl; 5 import std.exception:Exception,enforce,assumeUnique; 6 import std.conv:to; 7 import std.algorithm:countUntil,map,each; 8 import std.traits:EnumMembers; 9 import std.array:array,appender; 10 import std.format:format; 11 import std.variant:Algebraic; 12 import kprop.api.digitalocean.auth; // replace by your own key 13 import kprop.helper.prettyjson; 14 15 /** 16 Implemented in the D Programming Language 2015 by Laeeth Isharc and Kaleidic Associates 17 Boost Licensed 18 Use at your own risk - this is not tested at all and if you end up deleting all your 19 instances and creating 10,000 pricey new ones then it will not be my fault 20 */ 21 22 static this() 23 { 24 OceanRegions=[EnumMembers!OceanRegion].map!(a=>a.toString).array.assumeUnique; 25 DropletActions=[EnumMembers!DropletAction].map!(a=>a.toString).array.assumeUnique; 26 OceanImages= 27 [ 28 OceanGlobalImage(false,"CoreOS-766.4.0-(beta)",13578467,OceanDistro.CoreOS,""), 29 OceanGlobalImage(false,"CentOS-5.10-x64",6372321,OceanDistro.CentOS,""), 30 OceanGlobalImage(false,"5.10 x32",6372425, OceanDistro.CentOS), 31 OceanGlobalImage(false,"6.0 x64",6372581, OceanDistro.Debian), 32 OceanGlobalImage(false,"6.0 x32",6372662, OceanDistro.Debian), 33 OceanGlobalImage(false,"21 x64",9640922, OceanDistro.Fedora), 34 OceanGlobalImage(false,"10.1",10144573, OceanDistro.FreeBSD), 35 OceanGlobalImage(false,"12.04.5 x64",10321756, OceanDistro.Ubuntu), 36 OceanGlobalImage(false,"12.04.5 x32",10321777, OceanDistro.Ubuntu), 37 OceanGlobalImage(false,"7.0 x64",10322059, OceanDistro.Debian), 38 OceanGlobalImage(false,"7.0 x32",10322378, OceanDistro.Debian), 39 OceanGlobalImage(false,"7 x64",10322623, OceanDistro.CentOS), 40 OceanGlobalImage(false,"22 x64",12065782, OceanDistro.Fedora), 41 OceanGlobalImage(false,"15.04 x64",12658446, OceanDistro.Ubuntu), 42 OceanGlobalImage(false,"15.04 x32",12660649, OceanDistro.Ubuntu), 43 OceanGlobalImage(false,"8.1 x64",12778278, OceanDistro.Debian), 44 OceanGlobalImage(false,"8.1 x32",12778337, OceanDistro.Debian), 45 OceanGlobalImage(false,"14.04 x64",13089493, OceanDistro.Ubuntu), 46 OceanGlobalImage(false,"14.04 x32",13089823, OceanDistro.Ubuntu), 47 OceanGlobalImage(false,"6.7 x64",13090046, OceanDistro.CentOS), 48 OceanGlobalImage(false,"6.7 x32",13090097, OceanDistro.CentOS), 49 OceanGlobalImage(false,"10.2",13321858, OceanDistro.FreeBSD), 50 OceanGlobalImage(false,"815.0.0 (alpha)",13683512, OceanDistro.CoreOS), 51 OceanGlobalImage(false,"766.4.0 (stable)",13750582, OceanDistro.CoreOS), 52 OceanGlobalImage(false,"FreeBSD AMP on 10.1",10163059, OceanDistro.FreeBSD), 53 OceanGlobalImage(true,"Drone on 14.04",11774848, OceanDistro.Ubuntu,"Drone"), 54 OceanGlobalImage(true,"Cassandra on 14.04",12540744, OceanDistro.Ubuntu,"Cassandra"), 55 OceanGlobalImage(true,"ELK Logging Stack on 14.04",12542038, OceanDistro.Ubuntu,"ELK Logging"), 56 OceanGlobalImage(true,"Django on 14.04",12740667, OceanDistro.Ubuntu,"Django"), 57 OceanGlobalImage(true,"Mumble Server (murmur) on 14.04",12914152, OceanDistro.Ubuntu,"murmur"), 58 OceanGlobalImage(true,"Joomla! 3.4.3 on 14.04",13014869, OceanDistro.Ubuntu,"Joomla"), 59 OceanGlobalImage(true,"Magento-1.9.2.1 CE on 14.04",13115155, OceanDistro.Ubuntu,"Magento"), 60 OceanGlobalImage(true,"MongoDB 3.0.5 on 14.04",13115659, OceanDistro.Ubuntu,"MongoDB"), 61 OceanGlobalImage(true,"LEMP on 14.04",13138234, OceanDistro.Ubuntu,"LEMP"), 62 OceanGlobalImage(true,"LAMP on 14.04",13138235, OceanDistro.Ubuntu,"LAMP"), 63 OceanGlobalImage(true,"MediaWiki on 14.04",13185409, OceanDistro.Ubuntu,"MediaWiki"), 64 OceanGlobalImage(true,"WordPress on 14.04",13229890, OceanDistro.Ubuntu,"WordPress"), 65 OceanGlobalImage(true,"Ruby on Rails on 14.04 (Postgres, Nginx, Unicorn)",13400199, OceanDistro.Ubuntu,"Ruby on Rails"), 66 OceanGlobalImage(true,"MEAN on 14.04",13413549, OceanDistro.Ubuntu,"MEAN"), 67 OceanGlobalImage(true,"Drupal 7.39 on 14.04",13414327, OceanDistro.Ubuntu,"Drupal"), 68 OceanGlobalImage(true,"Docker 1.8.2 on 14.04",13495049, OceanDistro.Ubuntu,"Docker"), 69 OceanGlobalImage(true,"node v4.1.0 on 14.04",13586846, OceanDistro.Ubuntu,"nodejs"), 70 OceanGlobalImage(true,"Redis 3.0.4 on 14.04",13601457, OceanDistro.Ubuntu,"Redis"), 71 OceanGlobalImage(true,"ownCloud 8.1.3 on 14.04",13603669, OceanDistro.Ubuntu,"ownCloud"), 72 OceanGlobalImage(true,"GitLab 8.0.1 CE on 14.04",13670476, OceanDistro.Ubuntu,"GitLab"), 73 OceanGlobalImage(true,"Ghost 0.7.1 on 14.04",13750431, OceanDistro.Ubuntu,"Ghost"), 74 OceanGlobalImage(true,"Dokku v0.4.1 on 14.04",13750783, OceanDistro.Ubuntu,"Dokku"), 75 OceanGlobalImage(true,"Discourse on 14.04",13846109, OceanDistro.Ubuntu,"Discourse"), 76 OceanGlobalImage(true,"PHPMyAdmin on 14.04",11730661, OceanDistro.Ubuntu,"PHPMyAdmin"), 77 OceanGlobalImage(true,"Redmine on 14.04",12438838, OceanDistro.Ubuntu,"Redmine"), 78 ]; 79 } 80 81 string joinUrl(string url, string endpoint) 82 { 83 enforce(url.length>0, "broken url"); 84 if (url[$-1]=='/') 85 url=url[0..$-1]; 86 return url~"/"~endpoint; 87 } 88 /** 89 auto __str__(self): 90 return b"<{:s} at {:#x}>".format(type(self).__name__, id(self)) 91 92 auto __unicode__(self): 93 return "<{:s} at {:#x}>".format(type(self).__name__, id(self)) 94 */ 95 96 struct OceanAPI 97 { 98 string endpoint = "https://api.digitalocean.com/v2/"; 99 string token; 100 101 this(string token) 102 { 103 this.token=token; 104 } 105 this(string endpoint, string token) 106 { 107 this.endpoint=endpoint; 108 this.token=token; 109 } 110 } 111 112 JSONValue request(OceanAPI api, string url, HTTP.Method method=HTTP.Method.get, JSONValue params=JSONValue(null)) 113 { 114 enforce(api.token.length>0,"no token provided"); 115 url=api.endpoint.joinUrl(url); 116 auto client=HTTP(url); 117 client.addRequestHeader("Authorization", "Bearer "~api.token); 118 auto response=appender!(ubyte[]); 119 client.method=method; 120 switch(method) with(HTTP.Method) 121 { 122 case del: 123 client.setPostData(cast(void[])params.toString,"application/x-www-form-urlencoded"); 124 break; 125 case get,head: 126 client.setPostData(cast(void[])params.toString,"application/json"); 127 break; 128 default: 129 client.setPostData(cast(void[])params.toString,"application/json"); 130 break; 131 } 132 client.onReceive = (ubyte[] data) 133 { 134 response.put(data); 135 return data.length; 136 }; 137 client.perform(); // rely on curl to throw exceptions on 204, >=500 138 return parseJSON(cast(string)response.data); 139 } 140 141 142 // List all Actions 143 auto listActions(OceanAPI api) 144 { 145 return api.request("actions",HTTP.Method.get); 146 } 147 148 // retrieve existing Action 149 auto retrieveAction(OceanAPI api, string id) 150 { 151 return api.request("actions/"~id, HTTP.Method.get); 152 } 153 154 auto allNeighbours(OceanAPI api) 155 { 156 return api.request("reports/droplet_neighbors",HTTP.Method.get); 157 } 158 159 auto listUpgrades(OceanAPI api) 160 { 161 return api.request("droplet_upgrades",HTTP.Method.get); 162 } 163 164 // List all Domains (managed through Ocean DNS interface) 165 auto listDomains(OceanAPI api) 166 { 167 return api.request("domains", HTTP.Method.get); 168 } 169 170 struct OceanDomain 171 { 172 OceanAPI api; 173 string id; 174 alias id this; 175 this(OceanAPI api, string id) 176 { 177 this.api=api; 178 this.id=id; 179 } 180 // Create new Domain 181 static auto create(OceanAPI api, string name, string ip) 182 { 183 JSONValue params; 184 params["name"]=name; 185 params["ip_address"]=ip; 186 return api.request("domains", HTTP.Method.post, params); 187 } 188 auto request(string url, HTTP.Method method=HTTP.Method.get, JSONValue params=JSONValue(null)) 189 { 190 return api.request(url,method,params); 191 } 192 } 193 194 // Retrieve an existing Domain 195 auto get(OceanDomain domain) 196 { 197 return domain.request("domains/"~domain.id, HTTP.Method.get); 198 } 199 200 // Delete a Domain 201 auto del(OceanDomain domain) 202 { 203 return domain.request("domains/"~domain.id, HTTP.Method.del); 204 } 205 206 // List all Domain Records 207 auto listDomainRecords(OceanDomain domain) 208 { 209 return domain.request(format("domains/%s/records",domain.id), HTTP.Method.get); 210 } 211 212 // Create a new Domain Record 213 auto createRecord(OceanDomain domain, string rtype=null, string name=null, string data=null, 214 string priority=null, string port=null, string weight=null) 215 { 216 JSONValue params; 217 params["type"]=rtype; 218 if(name.length>0) 219 params["name"]=name; 220 if(data.length>0) 221 params["data"]=data; 222 if(priority.length>0) 223 params["priority"]=priority; 224 if(port.length>0) 225 params["port"]=port; 226 if(weight.length>0) 227 params["weight"]=weight; 228 return domain.request( 229 format("domains/%s/records",domain.id), HTTP.Method.post, params); 230 } 231 232 // Retrieve an existing Domain Record 233 auto getRecord(OceanDomain domain, string recordId) 234 { 235 return domain.request( 236 format("domains/%s/records/%s",domain.id,recordId), HTTP.Method.get); 237 } 238 239 // Delete a Domain Record 240 auto delRecord(OceanDomain domain, string recordId) 241 { 242 return domain.request( 243 format("domains/%s/records/%s",domain.id,recordId), HTTP.Method.del); 244 } 245 246 // Update a Domain Record 247 auto updateRecord(OceanDomain domain, string recordId,string name) 248 { 249 JSONValue params; 250 params["name"] = name; 251 return domain.request(format("domains/%s/records/%s",domain.id, recordId), HTTP.Method.put, params); 252 } 253 254 255 // list all droplets 256 auto listDroplets(OceanAPI api) 257 { 258 return api.request("droplets",HTTP.Method.get); 259 } 260 261 enum OceanRegion 262 { 263 ams2, 264 ams3, 265 fra1, 266 lon1, 267 nyc1, 268 nyc2, 269 nyc3, 270 sfo1, 271 sgp1, 272 tor1, 273 } 274 275 276 immutable string[] OceanRegions; 277 OceanRegion oceanRegion(string region) 278 { 279 OceanRegion ret; 280 auto i=OceanRegions.countUntil(region); 281 enforce(i>=0, new Exception("unknown droplet region: "~region)); 282 return cast(OceanRegion)i; 283 } 284 285 string toString(OceanRegion region) 286 { 287 final switch(region) with(OceanRegion) 288 { 289 case ams2: 290 return "Amsterdam 2"; 291 case ams3: 292 return "Amsterdam 3"; 293 case fra1: 294 return "Frankfurt 1"; 295 case lon1: 296 return "London 1"; 297 case nyc1: 298 return "New York 1"; 299 case nyc2: 300 return "New York 2"; 301 case nyc3: 302 return "New York 3"; 303 case sfo1: 304 return "San Francisco 1"; 305 case sgp1: 306 return "Singapore 1"; 307 case tor1: 308 return "Toronto 1"; 309 } 310 assert(0); 311 } 312 313 enum OceanDistro 314 { 315 CoreOS, 316 Debian, 317 Fedora, 318 CentOS, 319 FreeBSD, 320 Ubuntu, 321 } 322 alias OceanImageId=Algebraic!(int,string); 323 struct OceanGlobalImage 324 { 325 bool isApplication=false; 326 string slug; 327 int id; 328 OceanDistro distro; 329 string application; 330 } 331 OceanGlobalImage[] OceanImages; 332 333 334 struct Droplet 335 { 336 OceanAPI api; 337 int id; 338 339 this(OceanAPI api, int id) 340 { 341 this.api=api; 342 this.id=id; 343 } 344 345 string toString() 346 { 347 return id.to!string; 348 } 349 auto request(string uri, HTTP.Method method=HTTP.Method.get, JSONValue params=JSONValue(null)) 350 { 351 return api.request(uri,method,params); 352 } 353 354 // Create a new Droplet 355 static auto create(OceanAPI api,string name, OceanRegion region, string size, OceanImageId image, string[] sshKeys, string backups=null, 356 string ipv6=null, string privateNetworking=null, string userData=null) 357 { 358 JSONValue params; 359 params["name"]=name; 360 params["region"]=region.to!string; 361 params["size"]=size; 362 if(image.type==typeid(string)) 363 params["image"]=image.get!string; 364 else 365 params["image"]=image.get!int; 366 if (sshKeys.length>0) 367 params["ssh_keys"]=sshKeys; 368 if(backups.length>0) 369 params["backups"]=backups.to!bool; 370 if (ipv6.length>0) 371 params["ipv6"]=ipv6.to!bool; 372 if (privateNetworking.length>0) 373 params["private_networking"]=privateNetworking.to!bool; 374 if (userData.length>0) 375 params["user_data"]=userData; 376 return api.request("droplets", HTTP.Method.post, params); 377 } 378 } 379 380 // Makes an action 381 JSONValue action(Droplet droplet, DropletAction actionType,JSONValue params=JSONValue(null)) 382 { 383 params["type"]=actionType.toString; 384 return droplet.request(format("droplets/%s/actions",droplet.id), HTTP.Method.post, params); 385 } 386 387 enum DropletAction 388 { 389 reboot, 390 powerCycle, 391 shutdown, 392 powerOff, 393 powerOn, 394 passwordReset, 395 resize, 396 restore, 397 rebuild, 398 rename, 399 changeKernel, 400 enableIPv6, 401 disableBackups, 402 enablePrivateNetworking, 403 snapshot, 404 upgrade, 405 } 406 407 string toString(DropletAction action) 408 { 409 final switch(action) with(DropletAction) 410 { 411 case reboot: 412 return "reboot"; 413 case powerCycle: 414 return "power_cycle"; 415 case shutdown: 416 return "shutdown"; 417 case powerOff: 418 return "power_off"; 419 case powerOn: 420 return "power_on"; 421 case passwordReset: 422 return "password_reset"; 423 case resize: 424 return "resize"; 425 case restore: 426 return "restore"; 427 case rebuild: 428 return "rebuild"; 429 case rename: 430 return "rename"; 431 case changeKernel: 432 return "change_kernel"; 433 case enableIPv6: 434 return "enable_ipv6"; 435 case disableBackups: 436 return "disable_backups"; 437 case enablePrivateNetworking: 438 return "enable_private_networking"; 439 case snapshot: 440 return "snapshot"; 441 case upgrade: 442 return "upgrade"; 443 } 444 } 445 446 447 immutable string[] DropletActions; 448 DropletAction dropletAction(string action) 449 { 450 auto i=DropletActions.countUntil(action); 451 enforce(i>=0,new Exception("unknown droplet action: "~action)); 452 return cast(DropletAction)i; 453 } 454 455 struct OceanResult(T) 456 { 457 bool found; 458 T result; 459 } 460 // find droplet ID from anme 461 OceanResult!Droplet findDroplet(OceanAPI ocean, string name) 462 { 463 auto ret=ocean.Droplet(-1); 464 auto dropletResults=ocean.listDroplets; 465 auto droplets="droplets" in dropletResults; 466 enforce(droplets !is null, new Exception("bad response from Digital Ocean: "~dropletResults.prettyPrint)); 467 enforce((*droplets).type==JSON_TYPE.ARRAY, new Exception 468 ("bad response from Digital Ocean: "~dropletResults.prettyPrint)); 469 (*droplets).array.each!(a=>enforce(("name" in a.object) && a.object["name"].type==JSON_TYPE.STRING)); 470 auto i=(*droplets).array.map!(a=>a.object["name"].str).array.countUntil(name); 471 if (i==-1) 472 { 473 return OceanResult!Droplet(false,ret); 474 } 475 //auto p=("id" in ((*droplets).array[i])); 476 //enforce(p !is null, new Exception 477 // ("findDroplet cannot find id in results - malformed JSON?\n"~dropletResults.prettyPrint)); 478 return OceanResult!Droplet(true,ocean.Droplet((*droplets).array[i].object["id"].integer.to!int)); 479 } 480 481 // List all available Kernels for a Droplet 482 auto kernels(Droplet droplet) 483 { 484 return droplet.request(format("droplets/%s/kernels",droplet.id), HTTP.Method.get); 485 } 486 487 488 // Retrieve snapshots for a Droplet 489 auto snapshots(Droplet droplet) 490 { 491 return droplet.request(format("droplets/%s/snapshots",droplet.id), HTTP.Method.get); 492 } 493 494 // Retrieve backups for a Droplet 495 auto backups(Droplet droplet) 496 { 497 return droplet.request(format("droplets/%s/backups",droplet.id), HTTP.Method.get); 498 } 499 500 // Retrieve actions for a Droplet 501 auto actions(Droplet droplet) 502 { 503 return droplet.request(format("droplets/%s/actions",droplet.id), HTTP.Method.get); 504 } 505 506 // Retrieve an existing Droplet by id 507 auto retrieve(Droplet droplet) 508 { 509 return droplet.request("droplets/"~droplet.id.to!string, HTTP.Method.get); 510 } 511 512 // Delete a Droplet 513 auto del(Droplet droplet) 514 { 515 return droplet.request("droplets/"~droplet.id.to!string, HTTP.Method.del); 516 } 517 518 auto neighbours(Droplet droplet) 519 { 520 return droplet.request(format("droplets/%s/neighbors",droplet.id),HTTP.Method.get); 521 } 522 523 // Reboot a Droplet 524 auto reboot(Droplet droplet) 525 { 526 return droplet.action(DropletAction.reboot); 527 } 528 529 // Power Cycle a Droplet 530 auto powerCycle(Droplet droplet) 531 { 532 return droplet.action(DropletAction.powerCycle); 533 } 534 535 // Shutdown a Droplet 536 auto shutdown(Droplet droplet) 537 { 538 return droplet.action(DropletAction.shutdown); 539 } 540 541 // Power Off a Droplet 542 auto powerOff(Droplet droplet) 543 { 544 return droplet.action(DropletAction.powerOff); 545 } 546 547 // Power On a Droplet 548 auto powerOn(Droplet droplet) 549 { 550 return droplet.action(DropletAction.powerOn); 551 } 552 553 // Password Reset a Droplet 554 auto passwordReset(Droplet droplet) 555 { 556 return droplet.action(DropletAction.passwordReset); 557 } 558 559 // Resize a Droplet 560 auto resize(Droplet droplet, string size) 561 { 562 JSONValue params; 563 params["size"]=size; 564 return droplet.action(DropletAction.resize, params); 565 } 566 567 // Restore a Droplet 568 auto restore(Droplet droplet, string image) 569 { 570 JSONValue params; 571 params["image"]=image; 572 return droplet.action(DropletAction.restore, params); 573 } 574 575 // Rebuild a Droplet 576 auto rebuild(Droplet droplet, string image) 577 { 578 JSONValue params; 579 params["image"]=image; 580 return droplet.action(DropletAction.rebuild, params); 581 } 582 583 // Rename a Droplet 584 auto rename(OceanAPI api, Droplet droplet, string name) 585 { 586 JSONValue params; 587 params["name"]=name; 588 return droplet.action(DropletAction.rename,params); 589 } 590 591 // Change the Kernel 592 auto changeKernel(Droplet droplet, string kernel) 593 { 594 JSONValue params; 595 params["kernel"]=kernel; 596 return droplet.action(DropletAction.changeKernel, params); 597 } 598 599 // Enable IPv6 600 auto enableIPv6(Droplet droplet) 601 { 602 return droplet.action(DropletAction.enableIPv6); 603 } 604 605 // Disable Backups 606 auto disableBackups(Droplet droplet) 607 { 608 return droplet.action(DropletAction.disableBackups); 609 } 610 611 // Enable Private Networking 612 auto enablePrivateNetworking(Droplet droplet) 613 { 614 return droplet.action(DropletAction.enablePrivateNetworking); 615 } 616 617 // Snapshot 618 auto doSnapshot(Droplet droplet, string name=null) 619 { 620 JSONValue params; 621 if (name.length>0) 622 params["name"]=name; 623 return droplet.action(DropletAction.snapshot, params); 624 } 625 626 // Retrieve a Droplet Action 627 auto retrieveAction(Droplet droplet, string actionId) 628 { 629 return droplet.request( 630 format("droplets/%s/actions/%s",droplet.id, actionId), HTTP.Method.get); 631 } 632 633 auto upgrade(Droplet droplet) 634 { 635 JSONValue params; 636 params["upgrade"]=true; 637 droplet.action(DropletAction.upgrade,params); 638 } 639 struct OceanImage 640 { 641 OceanAPI api; 642 string id; 643 alias id this; 644 this(OceanAPI api, string id) 645 { 646 this.api=api; 647 this.id=id; 648 } 649 auto request(string uri, HTTP.Method method=HTTP.Method.get, JSONValue params=JSONValue(null)) 650 { 651 return api.request(uri,method,params); 652 } 653 } 654 // List all images 655 auto listImages(OceanAPI api) 656 { 657 return api.request("images", HTTP.Method.get); 658 } 659 660 // Retrieve an existing Image by id or slug 661 auto get(OceanImage image) 662 { 663 return image.request("images/"~image.id, HTTP.Method.get); 664 } 665 666 // Delete an Image 667 auto del(OceanImage image) 668 { 669 return image.request("images/"~image.id, HTTP.Method.del); 670 } 671 672 // Update an Image 673 auto update(OceanImage image, string name) 674 { 675 JSONValue params; 676 params["name"]=name; 677 return image.request("images/"~image.id, HTTP.Method.put, params); 678 } 679 680 // Transfer an Image 681 auto transfer(OceanImage image, OceanRegion region) 682 { 683 JSONValue params; 684 params["type"]="transfer"; 685 params["region"]=region.to!string; 686 return image.request(format("images/%s/actions",image.id), HTTP.Method.post,params); 687 } 688 689 // Retrieve an existing Image Action 690 691 auto getImageAction(OceanImage image, string actionId) 692 { 693 return image.request(format("images/%s/actions/%s",image.id,actionId), HTTP.Method.get); 694 } 695 696 struct OceanKey 697 { 698 OceanAPI api; 699 string value; 700 701 this(OceanAPI api,string key) 702 { 703 this.api=api; 704 this.value=key; 705 } 706 auto request(string uri, HTTP.Method method=HTTP.Method.get, JSONValue params=JSONValue(null)) 707 { 708 return api.request(uri,method,params); 709 } 710 711 // Create a new Key 712 static auto create(OceanAPI api, string name, string publicKey) 713 { 714 JSONValue params; 715 params["name"]=name; 716 params["public_key"]=publicKey; 717 return api.request("account/keys", HTTP.Method.post, params); 718 } 719 } 720 721 // list all keys 722 auto listKeys(OceanAPI api) 723 { 724 return api.request("account/keys", HTTP.Method.get); 725 } 726 727 728 // Retrieve an existing Key by Id or Fingerprint 729 730 auto retrieve(OceanKey key) 731 { 732 return key.request("account/keys/"~key.value, HTTP.Method.get); 733 } 734 735 // Update an existing Key by Id or Fingerprint 736 auto updateName(OceanKey key, string name) 737 { 738 JSONValue params; 739 params["name"]=name; 740 return key.request("account/keys/"~key.value, HTTP.Method.put, params); 741 } 742 743 // Destroy an existing Key by Id or Fingerprint 744 auto del(OceanKey key) 745 { 746 return key.request("account/keys/"~key.value, HTTP.Method.del); 747 } 748 749 750 // list all regions 751 auto listRegions(OceanAPI api) 752 { 753 return api.request("regions", HTTP.Method.get); 754 } 755 756 // list all sizes 757 auto listSizes(OceanAPI api) 758 { 759 return api.request("sizes", HTTP.Method.get); 760 }